﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RPGProject.Data_Structures;
namespace RPGProject.GameWorld.GameObjects
{
    /// <summary>
    /// The root of all objects in the world.
    /// </summary>
    abstract class GameObject : IGOEventListener, IPropertyRetrievable
    {
        public readonly String typeId;
        /// <summary>
        /// The type of GameObject it is
        /// </summary>
        public String TypeId
        {
            get { return this.typeId; }
            set { }
        }
        public readonly char icon;
        /// <summary>
        /// The icon on the console window for the GameObject
        /// </summary>
        public char Icon
        {
            get { return this.icon; }
            set { }
        }
        public ConsoleColor iconColor;
        /// <summary>
        /// The color of the icon for the GameObject
        /// </summary>
        public ConsoleColor IconColor
        {
            get { return this.iconColor; }
            set { }
        }
        /// <summary>
        /// A list of things to let know that properties have been updated
        /// </summary>
        private Vector<OnPropUpdate> onUpdates;
        /// <summary>
        /// List of properties
        /// </summary>
        private Dictionary<String, Object> properties;
        /// <summary>
        /// Initializes values for a GameObject.  Only to be used by derived classes.
        /// </summary>
        /// <param name="x">x coordinate to be intialized to.</param>
        /// <param name="y">y coordinate to be intialized to.</param>
        /// <param name="name"></param>
        /// <param name="type_id"></param>
        protected GameObject(int x, int y, char icon, ConsoleColor iconColor, String name, String type_id)
        {
            this.onUpdates = new Vector<OnPropUpdate>();
            this.typeId = type_id;
            this.icon = icon;
            this.iconColor = iconColor;
            this.properties = new Dictionary<string, object>();
            this.Properties["x"] = x;
            this.Properties["y"] = y;
            this.Properties["name"] = name;
        }

        public override string ToString()
        {
            return (String)this["name"];
        }
        /// <summary>
        /// Listens to a GameObjectEvent, from IGOEventListener.
        /// </summary>
        /// <param name="e">The event to listen to.</param>
        /// <returns>An event if needed.</returns>
        public abstract GameObjectEvent Listen(GameObjectEvent e);

        public static bool operator ==(GameObject go1, GameObject go2)
        {
            
            return /*go1.typeId == go2.typeId && */go1["x"].Equals(go2["x"]) && go1["y"].Equals(go2["y"]) && go1["name"].Equals(go2["name"]);
        }
        public static bool operator !=(GameObject go1, GameObject go2)
        {
            return !(go1 == go2);
        }

        public Dictionary<string, object> Properties
        {
            get { return this.properties; }
        }

        /// <summary>
        /// Indexer to look through properties.  Notifies changes.
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public object this[string s]
        {
            get
            {
                return this.Properties[s];
            }
            set
            {
                this.Properties[s] = value;
                this.OnUpdate(s, value);
            }
        }
        /// <summary>
        /// Takes a PropertyLiteral and attempts to parse it.
        /// </summary>
        /// <param name="lit"></param>
        /// <returns></returns>
        public object this[PropertyLiteral lit]
        {
            get
            {
                if (lit.Size == 1)
                    return this[lit.Front];
                else
                    return ((IPropertyRetrievable)this[lit.Front])[lit.Next];
            }
            set
            {
                if (lit.Size == 1)
                    this[lit.Front] = value;
                else
                    ((IPropertyRetrievable)this[lit.Front])[lit.Next] = value;
            }
        }
        /// <summary>
        /// Update helper function.
        /// </summary>
        /// <param name="s"></param>
        /// <param name="value"></param>
        private void OnUpdate(string s, object value)
        {
            foreach (OnPropUpdate opu in this.onUpdates)
            {
                opu(s, value);
            }
        }
        /// <summary>
        /// Adds a delegate to the list of delegates to call.
        /// </summary>
        /// <param name="onPropUpdate"></param>
        public void AddUpdate(OnPropUpdate onPropUpdate)
        {
            this.onUpdates.Add(onPropUpdate);
        }

    }
}
